﻿using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace EssentialWebFramework.Common.Attributes
{
    /// <summary>
    /// 防止重复提交的特性（仅适用于单机部署）
    /// </summary>
    public class AntiRepeatedCommitAttribute : ActionFilterAttribute
    {
        /// <summary>
        /// 缓存 TAG
        /// </summary>
        public const string CACHE_TAG = "AntiRC";

        /// <summary>
        /// 覆写 OnActionExecuting
        /// </summary>
        /// <param name="context"></param>
        public override void OnActionExecuting(ActionExecutingContext context)
        {
            ILogger logger = context.HttpContext.RequestServices.GetService<ILogger<AntiRepeatedCommitAttribute>>();
            IDistributedCache distributedCache = context.HttpContext.RequestServices.GetService<IDistributedCache>();
            CacheForComponent cache = new CacheForComponent(CACHE_TAG, distributedCache);

            string userId = context.HttpContext.User.Identity.Name;
            if (string.IsNullOrEmpty(userId))
            {
                userId = context.HttpContext.Connection.RemoteIpAddress.ToString();
            }

            var routeValues = context.ActionDescriptor.RouteValues;
            string requestTarget = routeValues["controller"] + "/" + routeValues["action"];

            // 将 用户id 和 请求的地址作为限制的指纹
            string fingerprint = string.Format("{0}-{1}", userId, requestTarget);

            bool isAlreadyCommit = cache.TryGetCacheValue(fingerprint, out string _);
            if (isAlreadyCommit)
            {
                logger.LogInformation("用户：{0} 访问 {1} 提交过于频繁，重复提交已被拦截", userId, requestTarget);
                context.Result = new OkObjectResult(new Result 
                { 
                    Success = false, 
                    Msg = "操作过于频繁，请稍后再试" 
                });
            }
            else
            {
                logger.LogInformation("用户：{0} 访问了 {1} （防止重复提交特性）", userId, requestTarget);
                cache.SetCacheSlideExpire(fingerprint, "", new TimeSpan(0, 0, 3));
            }
        }
    }
}
